
#include "hk_camera.h"

hk_camera::hk_camera(/* args */)
{
    cam_handle_ = nullptr;
    status_ = camera_status::DEV_INIT;
    dev_info_ = nullptr;
    is_open_ = false;
    is_grabbing_ = false;
    cap_thread_flag_ = false;
}

hk_camera::~hk_camera()
{
    stop();
}

bool hk_camera::cameraInit()
{
    if(!cameraFindDev()){
        return false;
    }
    if(!cameraOpenDev()){
        return false;
    }
    if(!cameraParamSet()){
        return false;
    }
    if(!cameraStartGrabbing()){
        return false;
    }
    return true;  
}

bool hk_camera::cameraFindDev()
{
    //todo 阻塞式枚举设备
    // ch:初始化SDK
    int nRet = MV_CC_Initialize();
    if (MV_OK != nRet)
    {
        printf("Initialize SDK fail! nRet [0x%x]\n", nRet);
        return false;
    }

    MV_CC_DEVICE_INFO_LIST stDeviceList;
    memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));

    // 枚举设备
    // enum device
    nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList);
    if (MV_OK != nRet)
    {
        printf("MV_CC_EnumDevices fail! nRet [%x]\n", nRet);
        return false;
    }
    if (stDeviceList.nDeviceNum > 0)
    {
        for (unsigned int i = 0; i < stDeviceList.nDeviceNum; i++)
        {
            printf("[device %d]:\n", i);
            MV_CC_DEVICE_INFO* pDeviceInfo = stDeviceList.pDeviceInfo[i];
            if (NULL == pDeviceInfo)
            {
                return false;
            } 
            printDeviceInfo(pDeviceInfo);            
        }  
    } 
    else
    {
        printf("Find No Devices!\n");
        return false;
    }
    dev_info_ = stDeviceList.pDeviceInfo[0];

    return true;
}

bool hk_camera::printDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo)
{
    if (NULL == pstMVDevInfo)
    {
        printf("The Pointer of pstMVDevInfo is NULL!\n");
        return false;
    }
    if (pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE)
    {
        int nIp1 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24);
        int nIp2 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16);
        int nIp3 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8);
        int nIp4 = (pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff);

        // ch:打印当前相机ip和用户自定义名字 | en:print current ip and user defined name
        printf("Device Model Name: %s\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chModelName);
        printf("CurrentIp: %d.%d.%d.%d\n" , nIp1, nIp2, nIp3, nIp4);
        printf("UserDefinedName: %s\n\n" , pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName);
    }
    else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE)
    {
        printf("Device Model Name: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chModelName);
        printf("UserDefinedName: %s\n\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chUserDefinedName);
    }
    else
    {
        printf("Not support.\n");
    }

    return true;
}

bool hk_camera::cameraOpenDev()
{
    // 选择设备并创建句柄
    // select device and create handle
    int nRet = MV_CC_CreateHandle(&cam_handle_, dev_info_);
    if (MV_OK != nRet)
    {
        printf("MV_CC_CreateHandle fail! nRet [%x]\n", nRet);
        return false;
    }

    // 打开设备
    // open device
    nRet = MV_CC_OpenDevice(cam_handle_);
    if (MV_OK != nRet)
    {
        printf("MV_CC_OpenDevice fail! nRet [%x]\n", nRet);
        return false;
    }
    printf("MV_CC_OpenDevice success!!!");
    // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)
    if (dev_info_->nTLayerType == MV_GIGE_DEVICE)
    {
        int nPacketSize = MV_CC_GetOptimalPacketSize(cam_handle_);
        if (nPacketSize > 0)
        {
            nRet = MV_CC_SetIntValue(cam_handle_,"GevSCPSPacketSize",nPacketSize);
            if(nRet != MV_OK)
            {
                printf("Warning: Set Packet Size fail nRet [0x%x]!\n", nRet);
            }
        }
        else
        {
            printf("Warning: Get Packet Size fail nRet [0x%x]!\n", nPacketSize);
        }
    }
    
    is_open_ = true;
    return true;
}

bool hk_camera::cameraCloseDev()
{
    if(!is_open_)
        return true;
    if(is_grabbing_)
        cameraStopGrabbing();
    // 关闭设备
    // close device
    int nRet = MV_CC_CloseDevice(cam_handle_);
    if (MV_OK != nRet)
    {
        printf("MV_CC_CloseDevice fail! nRet [%x]\n", nRet);
        return false;
    }
    printf("dev close!!!\n");
    return true;
}

bool hk_camera::cameraParamSet()
{
    // 设置帧率
    // todo
    // 设置触发模式为on
    // set trigger mode as on
    int nRet = MV_CC_SetEnumValue(cam_handle_, "TriggerMode", 1);
    if (MV_OK != nRet)
    {
        printf("MV_CC_SetTriggerMode fail! nRet [%x]\n", nRet);
        return false;
    }

    // 设置触发源
    // set trigger source
    //MV_TRIGGER_SOURCE_LINE0 LINE0 触发
    nRet = MV_CC_SetEnumValue(cam_handle_, "TriggerSource", MV_TRIGGER_SOURCE_LINE0);
    if (MV_OK != nRet)
    {
        printf("MV_CC_SetTriggerSource fail! nRet [%x]\n", nRet);
        return false;
    }

    // 设置RGB=0x02180014  Bayer BG 8 = 0x0108000B
    nRet = MV_CC_SetEnumValue(cam_handle_, "PixelFormat", 0x02180014);
    if (nRet != MV_OK) {
      printf("Pixel setting can't work.");
      return false;
    }
    return true;
}

bool hk_camera::cameraStartGrabbing()
{
    // 开始取流
    // start grab image
    if(!is_open_){
        printf("dev don't open!!!\n");
        return false;
    }

    if(is_grabbing_)
        return true;

    int nRet = MV_CC_StartGrabbing(cam_handle_);
    if (MV_OK != nRet)
    {
        printf("MV_CC_StartGrabbing fail! nRet [%x]\n", nRet);
        return false;
    }
    is_grabbing_ = true;
    return true;
}

bool hk_camera::cameraStopGrabbing()
{
    // 停止取流
    // end grab image
    if(!is_grabbing_){
        printf("dev grabbing is stop!!!\n");
        return true;
    }
    int nRet = MV_CC_StopGrabbing(cam_handle_);
    if (MV_OK != nRet)
    {
        printf("MV_CC_StopGrabbing fail! nRet [%x]\n", nRet);
        return false;
    }
    is_grabbing_ = false;
    printf("dev grabbing is stop!!!\n");
    return true;
}

bool hk_camera::stop()
{
    cap_thread_flag_ = false;
    if(cap_thread_ && cap_thread_->joinable()){
        cap_thread_->join();
        cap_thread_.reset();
    }

    if(cam_handle_ == nullptr)
        return true;
    cameraStopGrabbing();
    cameraCloseDev();
    // 销毁句柄
    // destroy handle
    int nRet = MV_CC_DestroyHandle(cam_handle_);
    if (MV_OK != nRet)
    {
        printf("MV_CC_DestroyHandle fail! nRet [%x]\n", nRet);
        // return false;
    }
    cam_handle_ = NULL;
    printf("MV_CC_DestroyHandle sucess!!!");
    MV_CC_Finalize();
    return true;
}

bool hk_camera::start()
{
    cameraInit();
    cap_thread_flag_ = true;
    cap_thread_.reset(new std::thread(std::bind(&hk_camera::capData, this, cam_handle_)));
    return true;
}

void hk_camera::capData(void *pUser)
{
    //尝试用线程，等待是否初始化完成
    if(!is_open_ || !is_grabbing_)
        return;
    MVCC_INTVALUE stParam;
    memset(&stParam, 0, sizeof(MVCC_INTVALUE));
    int nRet = MV_CC_GetIntValue(pUser, "PayloadSize", &stParam);
    if (MV_OK != nRet)
    {
        printf("Get PayloadSize fail! nRet [0x%x]\n", nRet);
        return;
    }

    MV_FRAME_OUT_INFO_EX stImageInfo;
    memset(&stImageInfo, 0, sizeof(MV_FRAME_OUT_INFO_EX));
    unsigned char * pData = (unsigned char *)malloc(sizeof(unsigned char) * stParam.nCurValue);
    if (NULL == pData)
    {
        return;
    }
    unsigned int nDataSize = stParam.nCurValue;

    printf("cap thread start!!!\n");


    while(cap_thread_flag_){
        int nRet = MV_OK;
        // nRet = MV_CC_SetCommandValue(pUser, "TriggerSoftware");
        // if(MV_OK != nRet)
        // {
        //     printf("failed in TriggerSoftware[%x]\n", nRet);
        // }

        nRet = MV_CC_GetOneFrameTimeout(pUser, pData, nDataSize, &stImageInfo, 1000);
        if (nRet == MV_OK)
        {
            // printf("GetOneFrame, Width[%d], Height[%d], nFrameNum[%d], nDataSize[%d]\n", 
            //     stImageInfo.nWidth, stImageInfo.nHeight, stImageInfo.nFrameNum, nDataSize);
            //todo callback
            if(cam_callback_)
                cam_callback_(stImageInfo.nHeight, stImageInfo.nWidth, pData);
        }
        else
        {
            printf("Get One Frame failed![%x]\n", nRet);
        }
    }

    if (pData != NULL)
    {
        free(pData);
        pData = NULL;
    }
}
